/*
*	This is Scythe's universal phs loader API, all the engine loaders use this
*/
#pragma once
#include "physLoadSave.h"

#include <math.h>
#include <string>
#include <vector>
using namespace std;
using std::string;
#include <fstream>

namespace Scythe
{

//each rigidbody is listed in one of these
class ModelOffset
{
public:
	int			id;
	Vector3		offsetPos;			//positional offset for each model.  model position = rigidbody position + offsetPos
	Matrix		offsetMat;			//offset from rigidbody's rotation
	ScytheActor*	rigidbody;			//index to the obj[] list of our associated rigidbody / actor
};


class SkeletonData
{
public:
	//character utility functions
	Vector3	getBonePosition( std::string boneName);
	Matrix	getBoneOrientation(std::string boneName);
	void		setBonePosition(std::string boneName, Vector3 pos, Matrix rot);	//inverse ragdoll, used to control the ragdoll bones directly
	sBody	getBoneActor(std::string boneName);		//returns the actor for the given bone, so you can do things manually 

	int			id;
	std::map<std::string, ScytheActor*>	bones;		//objects attached to bones are looked up by bone name
};

class PhysicalObject
{
	friend class PhysicsClass;
protected:

	void		destroy(const sScene);

	//update functions
	Vector3	getModelPos(int id);		//returns position of given model
	Matrix	getModelRotRows(int id);		//returns rotation as a transposed matrix
	Matrix	getModelRotColumns(int id);	//returns rotation
	Quat		getModelRotQuat(int id);		//returns rotation as a Quaternion

	//these two versions are mainly used internally by the above versions, but made public for special case uses such as ragdoll
	Vector3	getModelPos(int id, Scythe::Vector3 basePos, Scythe::Matrix baseRot);	//same thing, but allows you to give the rigidbody transform yourself, generally you wont need this
	Quat		getModelRotQuat(int id, Scythe::Matrix baseRot);					//same thing, but allows you to give the rigidbody transform yourself, generally you wont need this

	void		breakableJointTest();	//cause of joint breaking, only ODE needs to use it

private:
	vector<ModelOffset*>	models;		//list of all models transformation offsets
	vector<ScytheActor*>	obj;			//list of all physics bodies
	vector<sJoint>			joints;
	vector<SkeletonData*>	characterList;	//skeletal characters
};

//Callback function definitions
typedef void (*pfnJoint)(sJoint, const PhysicsJoint*, void*);
typedef void (*pfnBody)(PhysicalObject*, sBody, const PhysicsActor*, void*, int);
typedef void (*pfnModel)(PhysicalObject* , const char*, const PhysicsModel*, const int, const char*);
typedef void (*pfnSkel)(PhysicalObject* ,const char*, const PhysicsModel*, void*, SkeletonData*, const Vector3, const Matrix);
typedef void (*pfnLight)(PhysicalObject* ,const PhysicsLight*, const int);

/*
The physics engine control system
*/
class PhysicsClass
{
public:
	PhysicsClass();
	~PhysicsClass();

	//Actors have models attached to them.  Whenever a model is supposed to be loaded,
	//it will call this function and it will be up to you to load the appropriate model
	//it is optional, however without it you will only have physics, and no visuals
	void setLoadModelCallback(pfnModel func);
	void setLoadLightCallback(pfnLight func);
	//this is called after a new rigidbody is created, it is optional,
	//and is there to allow you to store the rigidbody pointer if you wish,
	//or make any changes to it
	void setBodyCreatedCallback(pfnBody func);
	//this is the same as the body callback, except for joints
	//also optional
	void setJointCreatedCallback(pfnJoint func);
	//similar to the load model callback, but specifically for skeletal models (ragdolls)
	void setLoadSkeletalModelCallback(pfnSkel func);


	//These functions create the given physics entity.  You can also set the position and rotation to place them at, and specify alternate callback functions
	bool		insert(PhysicalObject* obj, const sScene world, const PhysicsEntity* physEnt, const Vector3 position);
	bool		insert(PhysicalObject* obj, const sScene world, const PhysicsEntity* physEnt, const Vector3 position, const Quat rotation, pfnBody bodyCallbackOverride = 0, pfnJoint jointCallbackOverride = 0, void* bodyUserData = 0, void* jointUserData = 0, sSpace space = 0, void* skeletonUserData = 0);
	bool		insert(PhysicalObject* obj, const sScene world, const PhysicsEntity* physEnt, const Vector3 position, const Matrix rotation, pfnBody bodyCallbackOverride = 0, pfnJoint jointCallbackOverride = 0, void* bodyUserData = 0, void* jointUserData = 0, sSpace space = 0, void* skeletonUserData = 0);
	bool		insertT(PhysicalObject* obj, const sScene world, const PhysicsEntity* physEnt, const Vector3 position, const Matrix rotation, pfnBody bodyCallbackOverride = 0, pfnJoint jointCallbackOverride = 0, void* bodyUserData = 0, void* jointUserData = 0, sSpace space = 0, void* skeletonUserData = 0);		//transposed matrix

	pfnModel	modelCallback;
	pfnLight	lightCallback;
	pfnJoint	jointCallback;
	pfnBody	bodyCallback;
	pfnSkel	skeletalCallback;

protected:

	ScytheActor*	createNewActor1(const PhysicsActor *act, PhysicalObject* obj, const Vector3 position, const sScene nxScene, const Matrix rotation, pfnBody bodyCallbackOverride , void* bodyUserData , sSpace space = 0, SkeletonData* skeleton = 0);
	ScytheActor*	createNewActor(const PhysicsActor *act, const Vector3 position, const sScene nxScene, const Matrix rotation, pfnBody bodyCallbackOverride, sSpace space = 0, SkeletonData* skeleton = 0);
	sJoint		createJoint1(const PhysicsJoint* joint, const sBody act0, const sBody act1, const sScene nxScene, const Vector3 positionOffset, const Matrix rotationOffset, pfnJoint jointCallbackOverride = 0, void* jointUserData = 0, sJointParam jointParam = 0);
	sJoint		createJoint(const PhysicsJoint* joint, const sBody act0, const sBody act1, const sScene nxScene, const Vector3 positionOffset, const Matrix rotationOffset, sJointParam jointParam = 0);

	//character functions
	SkeletonData*		createSkeletalModel(PhysicalObject* obj, const PhysicsModel* model, const Vector3 position, const Matrix rotation, void* userData);

};
}
/**/